/*
 *	Copyright 2011 ja4cli
 *
 *	Licensed under the Apache License, Version 2.0 (the "License");
 *	you may not use this file except in compliance with the License.
 *	You may obtain a copy of the License at
 *
 *		http://www.apache.org/licenses/LICENSE-2.0
 *
 *	Unless required by applicable law or agreed to in writing, software
 *	distributed under the License is distributed on an "AS IS" BASIS,
 *	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *	See the License for the specific language governing permissions and
 *	limitations under the License.
 */
package org.skarby.ja4cli.conversion;

import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

import org.skarby.ja4cli.LocaleAware;

public abstract class NumberArgumentConverter<T extends Number> implements ArgumentConverter<T>, LocaleAware {
	private Locale locale;
	private NumberFormat formatter;
	private DecimalFormatSymbols symbols;
	
	public NumberArgumentConverter() {
		setLocale(Locale.ENGLISH);
	}

	/**
	 * Should extract the type specific value from the Number, ie.: invoking Number#floatValue().
	 * @param number the number to extract the type specific value from, which is <b>not</b> <code>null</code>..
	 * @return type specific value from the Number..
	 */
	protected abstract T extractValue(Number number);

	/**
	 * Can be overwritten to validate the passed argument (already trimmed for excessive white-space).
	 * @param argument the argument to validate, which is <b>not</b> <code>null</code> and trimmed for excessive white-space.
	 * @throws ArgumentConversionException if argument is invalid.
	 */
	protected void validate(String argument) throws ArgumentConversionException {
		// To be overwritten, when required.
	}
	
	@Override
	public T convert(String argument) throws ArgumentConversionException {
		if (argument == null) {
			return null;
		} else {
			argument = argument.trim();
			validate(argument);
			try {
				Number number = formatter.parse(argument);
				return extractValue(number);
			} catch (ParseException e) {
				throw new ArgumentConversionException("Unable to parse number from argument", e);
			}
		}
	}
	
	/**
	 * Retrieves the decimal separator for the supplied locale.
	 * @return the decimal separator for the supplied locale.
	 */
	protected char getDecimalSeparator() {
		return symbols.getDecimalSeparator();
	}
	
	/**
	 * Retrieves the thousands (groupings) separator for the supplied locale.
	 * @return the thousands (groupings) separator for the supplied locale.
	 */
	protected char getThousandsSeparator() {
		return symbols.getGroupingSeparator();
	}
	
	@Override
	public void setLocale(Locale locale) {
		this.locale = locale;
		formatter = NumberFormat.getInstance(locale);
		symbols = DecimalFormatSymbols.getInstance(locale);
	}

	@Override
	public Locale getLocale() {
		return locale;
	}
}
